package com.github.baichuan.application.scaffold.controller;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.baichuan.application.scaffold.domain.application.Application;
import com.github.baichuan.application.scaffold.domain.application.ApplicationService;
import com.github.baichuan.application.scaffold.domain.authorization.ActionService;
import com.github.baichuan.application.scaffold.domain.authorization.MenuService;
import com.github.baichuan.application.scaffold.domain.authorization.Resource;
import com.github.baichuan.application.scaffold.domain.authorization.RoleService;
import com.github.baichuan.application.scaffold.domain.identity.ApplicationMemberService;
import com.github.baichuan.application.scaffold.dto.application.InitForm;
import com.github.baichuan.application.scaffold.dto.membership.RegisterForm;
import com.github.baichuan.application.scaffold.dto.role.RoleForm;
import com.github.baichuan.application.scaffold.jpa.authorization.ResourceRepository;
import com.github.baichuan.application.scaffold.jpa.authorization.RoleRepository;
import com.github.baichuan.web.dto.Result;
import com.github.baichuan.web.format.TimeFormat;
import com.github.baichuan.web.servlet.Restful;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.web.bind.annotation.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;

@RestController
@Tag(name = "初始化工具")
@RequestMapping("/_initializer")
public class InitializerController {
    @Autowired
    private ApplicationService applicationService;
    @Autowired
    private ResourceRepository resourceRepository;
    @Autowired
    private MenuService menuService;
    @Autowired
    private ActionService actionService;
    @Autowired
    private RoleRepository roleRepository;
    @Autowired
    private RoleService roleService;
    @Autowired
    private ApplicationMemberService memberService;
    @Autowired
    private ObjectMapper jsonMapper;
    //https://cloud.tencent.com/developer/article/2221405?areaSource=&traceId=
    @Autowired
    private PlatformTransactionManager transactionManager;


    @PostMapping("/admin")
    @Operation(summary = "初始化系统管理员")
    public ResponseEntity<Result> init(@RequestBody @Valid InitForm form) throws IOException {
        Application application = applicationService.findByCode(form.getApplication());
        List<Resource> resources = resourceRepository.findByApplication(application);
        if(resources.size() == 0){
            return Restful.response(Result.fail("请先配置应用的资源菜单"));
        }
        if(roleRepository.findByApplication(application).size() > 0){
            return Restful.response(Result.fail("当前应用已配置角色"));
        }

        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setName("tx-register");
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus transaction = transactionManager.getTransaction(def);

        //创建系统管理员角色
        RoleForm roleForm = new RoleForm();
        roleForm.setName("系统管理员");
        roleForm.setIntro("系统初始化创建");
        Result result = roleService.add(form.getApplication(), roleForm);
        if(!result.isSuccess()){
            return Restful.response(result);
        }

        //将所有的菜单权限赋予系统管理员角色
        String roleId = (String)result.getData();
        roleRepository.adminAuthorize(application.getId(), roleId);

        //创建用户
        RegisterForm register = new RegisterForm();
        register.setLoginName(form.getLoginName().trim());
        register.setPassword(form.getPassword());
        register.setMemberType("ADMIN");
        register.setName("系统管理员");
        register.setExpire(LocalDate.now().plusMonths(1).format(TimeFormat.LOCAL_DATE));
        register.setRoles(Arrays.asList(roleId));
        result = memberService.add(application.getCode(), register);
        if(!result.isSuccess()){
            transactionManager.rollback(transaction);
            return Restful.response(result);
        }

        transactionManager.commit(transaction);
        return Restful.response(Result.success("默认管理员创建成功"));
    }

    @PostMapping("/{applicationCode}/resource")
    @Operation(summary = "初始化应用资源")
    @Transactional
    public ResponseEntity<Result> init(@PathVariable("applicationCode") String applicationCode) throws IOException {
        Application application = applicationService.findByCode(applicationCode);
        JsonNode array = readJson();
        for(JsonNode node: array){
            save(application, node, null);
        }
        return ResponseEntity.ok(Result.success("资源初始化成功"));
    }

    private JsonNode readJson() throws IOException {
        StringBuilder builder = new StringBuilder();
        InputStream resource = this.getClass().getClassLoader().getResourceAsStream("default-resource.json");
        BufferedReader reader = new BufferedReader(new InputStreamReader(resource, StandardCharsets.UTF_8));
        String line;
        while ((line = reader.readLine()) != null){
            builder.append(line);
            builder.append("\n");
        }
        return jsonMapper.readTree(builder.toString());
    }

    private void save(Application application, JsonNode node, Resource parent){
        Resource resource = null;
        if("Menu".equals(node.get("kind").asText())){
            resource = menuService.add(application, node, parent);
        } else {
            resource = actionService.add(application, node, parent);
        }

        Resource self = resource;
        if(node.hasNonNull("children")){
            node.get("children").elements().forEachRemaining(child -> save(application, child, self));
        }
    }
}
